package top.linyuxb.lycloud.config.starter.common;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.origin.OriginTrackedValue;
import top.linyuxb.lycloud.common.common.LyConfigJdbcService;
import top.linyuxb.lycloud.common.common.LyConfigServerService;
import top.linyuxb.lycloud.common.mapper.LyConfigClusterMapper;
import top.linyuxb.lycloud.common.mapper.LyConfigNodeMapper;
import top.linyuxb.lycloud.common.po.LyConfigClusterPo;
import top.linyuxb.lycloud.common.po.LyConfigNodePo;
import top.linyuxb.lycloud.common.po.LyConfigServerPo;
import top.linyuxb.lycloud.common.properties.LyConfigProperties;
import top.linyuxb.lycloud.common.utils.InetAddressUtil;
import top.linyuxb.lycloud.config.starter.dto.LyConfigListenerDto;
import top.linyuxb.lycloud.config.starter.dto.LyConfigServerDto;
import top.linyuxb.lycloud.config.starter.utils.StringToTypeConverterUtil;

import java.sql.Types;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by Intellij IDEA.
 *
 * @Author: linyuxb
 * @Date: 2024/6/13 15:36
 * @Desc: ly-config 公共配置
 */
@Slf4j
public class LyConfigService {
    // 注册中心，注册类型：1、内外ip注册；2、公网ip注册
    private static final Integer DEFAULT_REGISTER_TYPE = 1;
    /**
     * 存放属性和spring bean 映射
     */
    private static final Map<String, List<LyConfigServerDto>> servers = new HashMap<>();
    /**
     * 存放配置中心，配置内容，无变化则不更新
     */
    private static final Map<String, Object> configMap = new HashMap<>();
    /**
     * 配置修改监听集合
     */
    private static final List<LyConfigListenerDto> listenerList = new ArrayList<>();

    /**
     * 添加属性服务映射
     *
     * @param key
     * @param lyConfigServerDto
     */
    public static void saveServer(String key, LyConfigServerDto lyConfigServerDto) {
        List<LyConfigServerDto> orDefault = servers.getOrDefault(key, new ArrayList<>());
        orDefault.add(lyConfigServerDto);
        servers.put(key, orDefault);
    }

    /**
     * 添加配置修改监听
     *
     * @param lyConfigListenerDto
     */
    public static void saveListener(LyConfigListenerDto lyConfigListenerDto) {
        listenerList.add(lyConfigListenerDto);
    }

    /**
     * 获取属性服务
     *
     * @param key
     * @return
     */
    public static List<LyConfigServerDto> getServer(String key) {
        return servers.getOrDefault(key, new ArrayList<>());
    }

    /**
     * 获取全量服务
     *
     * @return
     */
    public static Map<String, List<LyConfigServerDto>> getAllServer() {
        return servers;
    }

    /**
     * 启动阶段 初始化spring配置
     *
     * @param map
     */
    public static void initValue(Map<String, Object> map) {
        Integer registerType = DEFAULT_REGISTER_TYPE;
        if (map.containsKey("ly-config.registerType")) {
            Object registerTypeObject = map.get("ly-config.registerType");
            if (registerTypeObject instanceof OriginTrackedValue registerTypeValue &&
                    registerTypeValue.getValue() instanceof Integer integerValue) {
                registerType = integerValue;
            }
        }
        Object envObject = map.get("ly-config.env");
        Object appObject = map.get("ly-config.app");
        if (Objects.nonNull(envObject) && envObject instanceof OriginTrackedValue envValue &&
                Objects.nonNull(appObject) && appObject instanceof OriginTrackedValue appValue) {
            Map<String, String> valueMap = getValueMaps(registerType,
                    envValue.getValue().toString(), appValue.getValue().toString());
            for (Map.Entry<String, String> stringStringEntry : valueMap.entrySet()) {
                String thisKey = stringStringEntry.getKey();
                String thisValue = stringStringEntry.getValue();
                if (map.containsKey(thisKey)) {
                    Object object = map.get(thisKey);
                    if (Objects.nonNull(object) && object instanceof OriginTrackedValue value) {
                        OriginTrackedValue originTrackedValue = OriginTrackedValue.of(thisValue, value.getOrigin());
                        map.put(thisKey, originTrackedValue);
                    }
                } else {
                    OriginTrackedValue originTrackedValue = OriginTrackedValue.of(thisValue);
                    map.put(thisKey, originTrackedValue);
                }
            }
        }
    }

    /**
     * 后置处理器 设置属性值
     *
     * @param registerType
     * @param lyConfigServerDto
     */
    public static void setValue(Integer registerType, LyConfigServerDto lyConfigServerDto) {
        // 获取当前ip
        String address = InetAddressUtil.getIpAddress(registerType);
        String thisKey = lyConfigServerDto.getKey();
        if (Objects.nonNull(lyConfigServerDto.getDefaultValue())) {
            thisKey = thisKey + ":" + lyConfigServerDto.getDefaultValue();
        }
        if (configMap.containsKey(thisKey)) {
            doSetValue(lyConfigServerDto, configMap.get(thisKey));
            return;
        }
        String sql = """
                SELECT * FROM ly_conf_node where `env` = ? and `app` = ? and `key` = ? 
                             and `status` = 1 and `type` != 0 order by `id` desc;
                """;
        List<LyConfigNodePo> query = LyConfigJdbcService.getJdbcTemplate().query(sql,
                new Object[]{lyConfigServerDto.getEnv(), lyConfigServerDto.getApp(), lyConfigServerDto.getKey()},
                new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR},
                new LyConfigNodeMapper());
        Map<String, String> clusterMap = getClusterMap(lyConfigServerDto.getEnv());
        // 赋值默认值
        Object value = lyConfigServerDto.getDefaultValue();
        String thisValue = getValue(query, address, clusterMap);
        if (StrUtil.isNotBlank(thisValue)) {
            value = StringToTypeConverterUtil.convertStringToType(
                    lyConfigServerDto.getField().getType(), thisValue);
        }
        if (Objects.nonNull(value)) {
            configMap.put(thisKey, value);
            doSetValue(lyConfigServerDto, value);
        }
    }

    /**
     * 定时任务 全量更新属性值
     */
    public static void updateValues(LyConfigProperties lyRpcClientProperties) {
        Map<String, String> valueMap = getValueMaps(lyRpcClientProperties.getRegisterType(),
                lyRpcClientProperties.getEnv(), lyRpcClientProperties.getApp());
        // 记录当前轮次更新过的key
        Map<String, Object> changeKeyValMap = MapUtil.newHashMap();
        servers.forEach((key, val) -> {
            // 当存在值时尝试替换、配置丢失，保持原数据
            if (valueMap.containsKey(key)) {
                Object value = StringToTypeConverterUtil.convertStringToType(
                        val.get(0).getField().getType(), valueMap.get(key));

                // 记录当下更新过的默认值key，若更新过后续都更新
                Set<String> keySets = new HashSet<>();
                for (int i = 0; i < val.size(); i++) {
                    LyConfigServerDto lyConfigServerDto = val.get(i);
                    String thisKey = lyConfigServerDto.getKey();
                    if (Objects.nonNull(lyConfigServerDto.getDefaultValue())) {
                        thisKey = thisKey + ":" + lyConfigServerDto.getDefaultValue();
                    }
                    if (!keySets.contains(thisKey) &&
                            configMap.containsKey(thisKey) &&
                            Objects.equals(value, configMap.get(thisKey))) {
                        continue;
                    }
                    keySets.add(thisKey);
                    changeKeyValMap.put(key, value);
                    configMap.put(thisKey, value);
                    doSetValue(lyConfigServerDto, value);
                }
            }
        });

        // 导入事件监听逻辑
        handleListener(changeKeyValMap, lyRpcClientProperties.getRegisterType());
    }

    /**
     * 获取全量配置信息
     *
     * @param registerType
     * @param env
     * @param app
     * @return
     */
    private static Map<String, String> getValueMaps(Integer registerType, String env, String app) {
        // 获取当前ip
        String address = InetAddressUtil.getIpAddress(registerType);
        String sql = """
                    SELECT * FROM ly_conf_node where `env` = ? and `app` = ? and `status` = 1 and `type` != 0 order by `id` desc;
                """;

        Map<String, List<LyConfigNodePo>> configNodeMap = LyConfigJdbcService.getJdbcTemplate().query(sql,
                        new Object[]{env, app},
                        new int[]{Types.VARCHAR, Types.VARCHAR},
                        new LyConfigNodeMapper()
                ).stream()
                .collect(Collectors.groupingBy(LyConfigNodePo::getKey));
        Map<String, String> clusterMap = getClusterMap(env);
        Map<String, String> valueMap = new HashMap<>();
        for (Map.Entry<String, List<LyConfigNodePo>> stringListEntry : configNodeMap.entrySet()) {
            String key = stringListEntry.getKey();
            List<LyConfigNodePo> val = stringListEntry.getValue();
            val.sort(Comparator.comparing(LyConfigNodePo::getId).reversed());
            String value = getValue(val, address, clusterMap);
            if (StrUtil.isNotBlank(value)) {
                valueMap.put(key, value);
            }
        }
        return valueMap;
    }

    /**
     * 获取配置
     *
     * @param val
     * @param address
     * @param clusterMap
     * @return
     */
    private static String getValue(List<LyConfigNodePo> val, String address, Map<String, String> clusterMap) {
        String value = StrUtil.EMPTY;
        for (LyConfigNodePo lyConfigNodePo : val) {
            // 1、全量生效
            if (lyConfigNodePo.getType() == 1) {
                value = lyConfigNodePo.getValue();
                break;
            }
            // 2、指定ip
            if (lyConfigNodePo.getType() == 2 && lyConfigNodePo.getServerInfos().contains(address)) {
                value = lyConfigNodePo.getValue();
                break;
            }
            // 3、指定集群
            if (lyConfigNodePo.getType() == 3) {
                String[] split = lyConfigNodePo.getServerInfos().split(",");
                for (String cluster : split) {
                    if (clusterMap.containsKey(cluster) && clusterMap.get(cluster).contains(address)) {
                        value = lyConfigNodePo.getValue();
                        break;
                    }
                }
            }
        }
        return value;
    }

    /**
     * 处理监听逻辑
     *
     * @param changeKeyValMap
     */
    private static void handleListener(Map<String, Object> changeKeyValMap, Integer registerType) {
        for (LyConfigListenerDto lyConfigListenerDto : listenerList) {
            boolean status = false;
            // 全量匹配，绑定最后一个参数
            Object value = null;
            switch (lyConfigListenerDto.getChangeTypeEnum()) {
                case ANY -> {
                    for (String key : lyConfigListenerDto.getKeys()) {
                        if (changeKeyValMap.containsKey(key)) {
                            status = true;
                            value = changeKeyValMap.get(key);
                            break;
                        }
                    }
                }
                case ALL -> {
                    status = true;
                    for (String key : lyConfigListenerDto.getKeys()) {
                        if (!changeKeyValMap.containsKey(key)) {
                            status = false;
                            break;
                        }
                        value = changeKeyValMap.get(key);
                    }
                }
            }
            if (status && Objects.nonNull(value)) {
                String ipAddress = InetAddressUtil.getIpAddress(registerType);
                List<LyConfigServerPo> lyConfigServerPos =
                        LyConfigServerService.getServers(lyConfigListenerDto.getEnv()).get(lyConfigListenerDto.getApp());
                if (CollUtil.isEmpty(lyConfigServerPos)) {
                    return;
                }
                if (lyConfigServerPos.size() == 1 &&
                        Objects.equals(ipAddress, lyConfigServerPos.get(0).getServerIp())) {
                    ReflectUtil.invoke(lyConfigListenerDto.getBean(), lyConfigListenerDto.getMethod());
                    return;
                }
                int hashCode = value.hashCode();
                int pos = hashCode % lyConfigServerPos.size();
                if(Objects.equals(ipAddress, lyConfigServerPos.get(pos).getServerIp())){
                    ReflectUtil.invoke(lyConfigListenerDto.getBean(), lyConfigListenerDto.getMethod());
                }
            }
        }
    }

    /**
     * 获取集群map
     *
     * @param env
     * @return
     */
    private static Map<String, String> getClusterMap(String env) {
        return LyConfigJdbcService.getJdbcTemplate().query(
                        """
                                    SELECT * FROM ly_conf_cluster where `env` = ? and `status` = 1 order by `id` desc;
                                """,
                        new Object[]{env},
                        new int[]{Types.VARCHAR},
                        new LyConfigClusterMapper()).stream()
                .collect(Collectors.toMap(
                        LyConfigClusterPo::getClusterKey,
                        LyConfigClusterPo::getClusterIps,
                        (ips1, ips2) -> ips1
                ));
    }

    /**
     * 反射修改spring bean 属性
     *
     * @param lyConfigServerDto
     * @param value
     */
    private static void doSetValue(LyConfigServerDto lyConfigServerDto, Object value) {
        try {
            lyConfigServerDto.getField().setAccessible(true);
            lyConfigServerDto.getField().set(lyConfigServerDto.getBean(), value);
        } catch (Exception e) {
            log.error("LyConfigCommon setValue", e);
        }
    }

}
